﻿using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Media.PhoneExtensions;
using NiklasKarl.WarpCam.Effects;
using NiklasKarl.WarpCam.Models;
using NiklasKarl.WarpCam.Native;
using NiklasKarl.WarpCam.Sources;
using Nokia.Graphics.Imaging;
using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Navigation;
using Windows.ApplicationModel;
using Windows.Phone.Media.Capture;
using Windows.Storage;
using Windows.Storage.Streams;

namespace NiklasKarl.WarpCam.Pages
{
    public partial class CapturePage : PhoneApplicationPage
    {
        private ISinglePreview m_renderer;

        private PhoneApplicationFrame m_frame;
        private object m_frameContentWhenOpened;
        private PreviewPage m_previewPage;

        private Uri m_previewUri;
        private string m_sharePath;


        #region EffectIndex

        public static readonly DependencyProperty EffectIndexProperty = DependencyProperty.Register(
            "EffectIndex", typeof(int), typeof(CapturePage), new PropertyMetadata(-1, OnEffectIndexPropertyChanged));

        public int EffectIndex
        {
            get { return (int)GetValue(EffectIndexProperty); }
            set { SetValue(EffectIndexProperty, value); }
        }

        private static void OnEffectIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as CapturePage).OnEffectIndexChanged(d, new EventArgs());
        }

        private void OnEffectIndexChanged(object sender, EventArgs e)
        {
            if (m_renderer != null)
            {
                ICameraEffect effect = null;
                if (EffectIndex >= 0 && EffectIndex <= 9)
                {
                    effect = App.Model.Effects[EffectIndex];
                }

                // m_renderer.Effect = effect;
            }
        }

        #endregion


        public CapturePage()
        {
            InitializeComponent();
        }

        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (m_renderer != null)
            {
                if (m_renderer.PreviewSource != App.Model.CameraSource)
                {
                    m_renderer.PreviewSource = null;

                    await SetCameraSourceAsync();
                }
            }
            else
            {
                if (State.ContainsKey("EffectIndex"))
                {
                    EffectIndex = (int)State["EffectIndex"];
                }
            }

            base.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            if (e.NavigationMode != NavigationMode.Back)
            {
                State["EffectIndex"] = EffectIndex;
            }

            base.OnNavigatedFrom(e);
        }


        private async void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
        {
            if (m_renderer == null)
            {
                SetStatus(true, "Initializing...");

                InitializePreviewRenderer();

                await SetCameraSourceAsync();

                SetStatus(false, null);
            }
        }

        private void InitializePreviewRenderer()
        {
            AppModel model = App.Model;

            /*if (EffectIndex >= 0 && EffectIndex <= 9)
            {
                m_source.Effect = model.Effects[EffectIndex];
            }*/

            PreviewFactory factory = new PreviewFactory();

            m_renderer = factory.CreateSinglePreview();
            m_renderer.PreviewRect = new Windows.Foundation.Rect(0.0, 0.0, 480.0, 640.0);

            LayoutRoot.SetBackgroundContentProvider(m_renderer.CreateContentProvider());
            LayoutRoot.SetBackgroundManipulationHandler(m_renderer);
        }

        private async Task SetCameraSourceAsync()
        {
            AppModel model = App.Model;

            await Task.Run(() =>
            {
                model.CameraSync.WaitOne();
            });

            m_renderer.PreviewRotation = model.CameraRotation;
            m_renderer.PreviewFlip = model.CameraFlip;

            m_renderer.PreviewSource = model.CameraSource;
        }


        private async void SwitchCamera_Click(object sender, RoutedEventArgs e)
        {
            DisableButtons();

            SetStatus(true, "Switching...");

            await SwitchCameraAsync();

            SetStatus(false, null);

            EnableButtons();
        }

        private Task SwitchCameraAsync()
        {
            AppModel model = App.Model;

            m_renderer.PreviewSource = null;

            return model.SwitchLocationAsync().ContinueWith((t) =>
            {
                return SetCameraSourceAsync(); 
            }).Unwrap();
        }


        private async void CaptureButton_Click(object sender, RoutedEventArgs e)
        {
            DisableButtons();

            SetStatus(true, "Capturing...");

            await CaptureAsync();

            SetStatus(false, null);

            OpenPreviewPage();

            EnableButtons();
        }

        private void DisableButtons()
        {
            CaptureButton.IsEnabled = false;
            SwitchButton.IsEnabled = false;
        }

        private void EnableButtons()
        {
            CaptureButton.IsEnabled = true;
            SwitchButton.IsEnabled = true;
        }

        private async Task CaptureAsync()
        {
            m_previewUri = null;
            m_sharePath = null;

            AppModel model = App.Model;
            ICameraEffect effect = model.Effects[EffectIndex];

            using (MemoryStream stream = new MemoryStream())
            {
                CameraCaptureSequence sequence = model.CameraDevice.CreateCaptureSequence(1);
                sequence.Frames[0].CaptureStream = stream.AsOutputStream();

                await model.CameraDevice.PrepareCaptureSequenceAsync(sequence);
                await sequence.StartCaptureAsync();

                IBuffer buffer = stream.GetWindowsRuntimeBuffer();

                if (effect != null)
                {
                    buffer = await effect.GetJpegImage(
                        new BufferImageSource(buffer),
                        Rotation.Rotate0
                        );
                }
                else
                {
                    buffer = await JpegTools.FlipAndRotateAsync(
                        buffer,
                        model.CameraFlip,
                        model.CameraRotation,
                        JpegOperation.AllowLossy
                        );
                }

                MediaLibrary library = new MediaLibrary();
                Picture picture = library.SavePictureToCameraRoll("WarpCamImage", buffer.AsStream());

                StorageFolder folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("TemporaryImages", CreationCollisionOption.OpenIfExists);
                StorageFile file = await folder.CreateFileAsync("PreviewImage.jpeg", CreationCollisionOption.GenerateUniqueName);

                using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
                {
                    await fileStream.WriteAsync(buffer);
                    await fileStream.FlushAsync();
                }

                m_previewUri = new Uri(file.Path, UriKind.Absolute);
                m_sharePath = picture.GetPath();
            }
        }

        private void OpenPreviewPage()
        {
            if (m_frame == null)
            {
                m_frame = App.Current.RootVisual as PhoneApplicationFrame;

                if (m_frame != null)
                {
                    m_frameContentWhenOpened = m_frame.Content;

                    m_frame.Navigated += Frame_Navigated;
                    m_frame.NavigationFailed += Frame_NavigationStoppedOrFailed;
                    m_frame.NavigationStopped += Frame_NavigationStoppedOrFailed;

                    m_frame.Navigate(new Uri("/Pages/PreviewPage.xaml", UriKind.Relative));
                }
            }
        }

        private void ClosePreviewPage()
        {
            if (m_frame != null)
            {
                m_frame.Navigated -= Frame_Navigated;
                m_frame.NavigationFailed -= Frame_NavigationStoppedOrFailed;
                m_frame.NavigationStopped -= Frame_NavigationStoppedOrFailed;

                m_frame = null;
                m_frameContentWhenOpened = null;
            }

            m_previewPage = null;
        }

        private void Frame_Navigated(object sender, NavigationEventArgs e)
        {
            if (e.Content == m_frameContentWhenOpened)
            {
                ClosePreviewPage();
            }
            else if (m_previewPage == null)
            {
                m_previewPage = e.Content as PreviewPage;

                if (m_previewPage != null)
                {
                    m_previewPage.ImageUri = m_previewUri;
                    m_previewPage.SharePath = m_sharePath;
                }
            }
        }

        private void Frame_NavigationStoppedOrFailed(object sender, EventArgs e)
        {
            ClosePreviewPage();
        }


        private void SetStatus(bool isWorking, string statusText)
        {
            if (isWorking)
            {
                SystemTray.ProgressIndicator.Text = statusText;
            }

            SystemTray.ProgressIndicator.IsVisible = isWorking;
        }
    }
}